package io.github.fb513.api.facade;

import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper;
import io.github.fb513.api.entity.*;
import io.github.fb513.api.enums.ActiveUserStatusEnum;
import io.github.fb513.api.enums.MessageStatusEnum;
import io.github.fb513.api.model.response.OfflineMsgResp;
import io.github.fb513.api.queue.MessageSender;
import io.github.fb513.api.service.*;
import io.github.fb513.common.enums.CommandEnum;
import io.github.fb513.common.model.BaseData;
import io.github.fb513.common.model.MessageData;
import io.github.fb513.common.model.MessageInfo;
import io.github.fb513.common.model.MessageRespData;
import io.github.fb513.common.util.ImUtils;
import io.github.fb513.common.util.JsonParser;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import org.springframework.transaction.annotation.Transactional;

import java.util.List;

/**
 * @author fengbo
 * @since 2023/12/27 16:06
 */
@Component
@Slf4j
public class MessageFacade {

    @Autowired private IMessageService messageService;
    @Autowired private IUserMessageService userMessageService;
    @Autowired private IUserService userService;
    @Autowired private IUserOnlineService userOnlineService;
    @Autowired private IFriendshipService friendshipService;
    @Autowired private MessageSender messageSender;
    @Autowired private JsonParser jsonParser;

    @Transactional(rollbackFor = Exception.class)
    public void sendMsg(BaseData<?> baseData) {
        Long userId = baseData.getUserId();
        Integer clientType = baseData.getClientType();
        User sourceUser = userService.getOne(new LambdaQueryWrapper<User>()
                .select(User::getId)
                .eq(User::getId, userId)
        );
        // 判断是否禁言等 TODO
        // 判断用户关系链 TODO

        MessageData messageData = (MessageData) baseData.getContent();
        // 保存消息
        Message message = new Message();
        message.setClientId(messageData.getClientId());
        message.setMessageInfo(messageData.getMessage());
        message.setMessageType(messageData.getMessageType());
        messageService.save(message);

        UserMessage sourceMessage = new UserMessage();
        sourceMessage.setOwnerId(userId);
        sourceMessage.setSourceId(messageData.getTargetId());
        sourceMessage.setSourceType(0);
        sourceMessage.setMessageId(message.getId());
        sourceMessage.setMessageStatus(MessageStatusEnum.READ.getCode());
        userMessageService.save(sourceMessage);

        UserMessage targetMessage = new UserMessage();
        targetMessage.setOwnerId(messageData.getTargetId());
        targetMessage.setSourceId(userId);
        targetMessage.setSourceType(1);
        targetMessage.setMessageId(message.getId());
        targetMessage.setMessageStatus(MessageStatusEnum.UN_RECEIVE.getCode());
        userMessageService.save(targetMessage);

        friendshipService.update(new LambdaUpdateWrapper<Friendship>()
                .set(Friendship::getMaxMessageId, targetMessage.getId())
                .eq(Friendship::getOwnerId, messageData.getTargetId())
                .eq(Friendship::getFriendId, userId)
                .lt(Friendship::getMaxMessageId, targetMessage.getId())
        );
        friendshipService.update(new LambdaUpdateWrapper<Friendship>()
                .set(Friendship::getMaxMessageId, sourceMessage.getId())
                .set(Friendship::getReadMessageId, sourceMessage.getId())
                .eq(Friendship::getOwnerId, userId)
                .eq(Friendship::getFriendId, messageData.getTargetId())
        );

        try {
            sendMsg(userId, clientType, sourceMessage.getId(), targetMessage.getId(), messageData);
        } catch (Throwable e) {
            log.error("发送消息失败", e);
        }
    }

    private void sendMsg(Long userId, Integer clientType, Long sourceMsgId, Long targetMsgId, MessageData messageData) {
        // 发送消息
        List<UserOnline> activeUserList = userOnlineService.list(new LambdaQueryWrapper<UserOnline>()
                .select(UserOnline::getUserId, UserOnline::getClientType, UserOnline::getBrokerHost, UserOnline::getBrokerPort)
                .in(UserOnline::getUserId, messageData.getTargetId(), userId)
                .eq(UserOnline::getActiveStatus, ActiveUserStatusEnum.ONLINE.getCode())
        );
        for (UserOnline activeUser : activeUserList) {
            int command;
            BaseData<?> respData;
            if (activeUser.getUserId().equals(userId) && activeUser.getClientType().equals(clientType)) {
                // 服务端收到信息的回复
                MessageRespData messageRespData = new MessageRespData();
                messageRespData.setMessageId(sourceMsgId);
                messageRespData.setClientId(messageData.getClientId());
                messageRespData.setSourceId(userId);
                messageRespData.setTargetId(messageData.getTargetId());
                command = CommandEnum.SERVER_MESSAGE_ACK.getCommand();
                respData = new BaseData<>(userId, clientType, messageRespData);
            } else {
                command = CommandEnum.MESSAGE.getCommand();
                MessageData messageRespData = new MessageData();
                messageRespData.setMessageId(targetMsgId);
                messageRespData.setClientId(messageData.getClientId());
                messageRespData.setSourceId(userId);
                if (activeUser.getUserId().equals(userId)) {
                    messageRespData.setSourceType(0);
                } else {
                    messageRespData.setSourceType(1);
                }
                messageRespData.setTargetId(messageData.getTargetId());
                messageRespData.setMessage(messageData.getMessage());
                messageRespData.setMessageType(messageData.getMessageType());
                respData = new BaseData<>(activeUser.getUserId(), activeUser.getClientType(), messageRespData);
            }
            messageSender.sendAsync(ImUtils.getBrokerKey(activeUser.getBrokerHost(), activeUser.getBrokerPort()),
                    jsonParser.formatObjToJson(MessageInfo.getJsonMessage(command, respData)));
        }
    }

    public void receiveMsg(BaseData<?> baseData) {
        UserMessage userMessage = getUserMessage(baseData);
        if (userMessage == null) {
            return;
        }
        userMessageService.update(new LambdaUpdateWrapper<UserMessage>()
                .set(UserMessage::getMessageStatus, MessageStatusEnum.RECEIVED.getCode())
                .eq(UserMessage::getId, userMessage.getId())
                .eq(UserMessage::getMessageStatus, MessageStatusEnum.UN_RECEIVE.getCode())
        );
    }

    public void readMsg(BaseData<?> baseData) {
        UserMessage userMessage = getUserMessage(baseData);
        if (userMessage == null) {
            return;
        }
        userMessageService.update(new LambdaUpdateWrapper<UserMessage>()
                .set(UserMessage::getMessageStatus, MessageStatusEnum.READ.getCode())
                .eq(UserMessage::getId, userMessage.getId())
        );
        friendshipService.update(new LambdaUpdateWrapper<Friendship>()
                .set(Friendship::getReadMessageId, userMessage.getId())
                .eq(Friendship::getOwnerId, userMessage.getOwnerId())
                .eq(Friendship::getFriendId, userMessage.getSourceId())
                .lt(Friendship::getReadMessageId, userMessage.getId())
        );
    }

    private UserMessage getUserMessage(BaseData<?> baseData) {
        MessageRespData messageRespData = (MessageRespData) baseData.getContent();
        return userMessageService.getOne(new LambdaQueryWrapper<UserMessage>()
                .select(UserMessage::getId, UserMessage::getOwnerId, UserMessage::getSourceId, UserMessage::getMessageId)
                .eq(UserMessage::getOwnerId, baseData.getUserId())
                .eq(UserMessage::getId, messageRespData.getMessageId())
                .last("limit 1")
        );
    }

    public List<OfflineMsgResp> getOfflineMessageList(Long userId, Long sourceId, Long maxMsgId) {
        return userMessageService.getMessageList(userId, sourceId, maxMsgId, null);
    }
}
